<script setup>
// 引入three
import * as THREE from 'three'
// 引入轨道控制器
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
// 引入gltf模型加载库GLTFLoader.js
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
// 引入gltf解析器（压缩过的模型要使用解析器！！！）
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'
// 引入hdr加载器
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js'
// 引入描边器？
import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass'
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass'
// 引入补间动画函数
import gsap from 'gsap'

// 引入弹窗组件
import dataPopup from './dataPopup.vue'

import { dataSet } from '@/stores'
const dataSets = dataSet()

// 初始化三维场景
const scene = new THREE.Scene()
scene.background = new THREE.Color('#ccc') // 设置背景色

// 初始化相机
const camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
)

// 设置相机位置
camera.position.set(-2.79626343630831, 1.1721123853734845, 2.5885392090220933)


// 初始化渲染器
const render = new THREE.WebGLRenderer({
    antialias: true // 设置抗锯齿
})
// 设置渲染器大小
render.setSize(window.innerWidth, window.innerHeight)
render.shadowMap.enabled = true
render.shadowMap.type = THREE.PCFSoftShadowMap
document.body.appendChild(render.domElement)


// 初始化轨道控制器
const controls = new OrbitControls(camera, render.domElement)
// 开启阻尼效果
controls.enableDamping = true
// 设置阻尼系数
controls.dampingFactor = 0.05
// 限制垂直旋转角度
controls.minPolarAngle = 0
controls.maxPolarAngle = Math.PI / 2
// 禁止水平移动
controls.enablePan = false
// 设置相机向外移动的最大值
controls.maxDistance = 10


// 加载hdr贴图
const rgbeloader = new RGBELoader()
rgbeloader.load('/textures/hdr/20.hdr', (envMap) => {
    // 设置球形贴图
    envMap.mapping = THREE.EquirectangularReflectionMapping
    // 设置环境贴图
    scene.background = envMap
    // 设置环境贴图
    scene.environment = envMap
})

// 封装子网格筛选函数
const addToGroup = (child, group, names) => {
    if (names.includes(child.name)) {
        group.push(child)
    }
}
const addToPersonage = (child, group, names) => {
    if (!names.includes(child.name)) {
        group.push(child)
    }
}
// 创建GLTF加载器对象
const gltfLoader = new GLTFLoader()
// 创建解析器对象
const dracoLoader = new DRACOLoader()
// 解析器位置
dracoLoader.setDecoderPath('./draco/')
// 给加载器指定解析器
gltfLoader.setDRACOLoader(dracoLoader)
// 整个车身
const carbody = []
// 整个模型
let carRoot
gltfLoader.load('./model/car3.glb', (glb) => {
    carRoot = glb.scene
    carRoot.traverse((child) => {
        if (child.isMesh) {
            carbody.push(child)
            // 备份子网格颜色
            dataSets.originalColors[child.uuid] = child.material.color.clone() // 以 UUID 作为唯一标识
            // 备份子网格材质
            child.userData.originalMaterial = child.material
        }

        // 车轮
        addToGroup(child, dataSets.carhub, dataSets.wheelNames)
        addToGroup(child, dataSets.cartyre, dataSets.tireNames)
        // 车门
        addToGroup(child, dataSets.leftdoors, dataSets.leftdoorName)
        addToGroup(child, dataSets.rightdoors, dataSets.rightdoorName)
        // 车灯
        addToGroup(child, dataSets.headlights, dataSets.headlightNmae)
        addToGroup(child, dataSets.taillights, dataSets.taillightName)
        // 车衣
        addToGroup(child, dataSets.coatPictures, dataSets.coatPictureName)
        // 虚化
        addToPersonage(child, dataSets.gallardos, dataSets.gallardoName)
    })

    scene.add(carRoot)

    // 隐藏遮罩层
    dataSets.loadingShade = false
})


// 加载道路纹理
const textureLoader = new THREE.TextureLoader()
const roadTexture = textureLoader.load('/textures/img/chedao.jpg')
// 设置重复模式
roadTexture.wrapS = THREE.RepeatWrapping
roadTexture.wrapT = THREE.RepeatWrapping

// 创建地面几何体并应用纹理
const geometry = new THREE.PlaneGeometry(5, 50) // 大小可以根据需要调整
const material = new THREE.MeshBasicMaterial({
    transparent: true,
    opacity: 0.1,
    side: THREE.DoubleSide, // 确保地面正反两面都能看到纹理
    map: roadTexture
})
const road = new THREE.Mesh(geometry, material)
road.rotation.x = -Math.PI / 2 // 使地面平行于X-Z平面
scene.add(road)

// 添加平行光源
const ligth1 = new THREE.DirectionalLight(0xffffff, 1)
ligth1.position.set(0, 10, 0)
const ligth2 = new THREE.DirectionalLight(0xffffff, 2)
ligth2.position.set(10, 10, 0)
const ligth3 = new THREE.DirectionalLight(0xffffff, 2)
ligth3.position.set(-10, 10, 0)
scene.add(ligth1, ligth2, ligth3)


// 描边部分
// 创建渲染器
const composer = new EffectComposer(render)
composer.addPass(new RenderPass(scene, camera))
// 设置描边效果
const outlinePass = new OutlinePass(new THREE.Vector2(window.innerWidth, window.innerHeight), scene, camera)
outlinePass.edgeStrength = 10 // 描边强度
outlinePass.edgeGlow = 0.5 // 描边发光效果
outlinePass.visibleEdgeColor.set(0xff0000) // 可见边颜色
outlinePass.hiddenEdgeColor.set(0x000000) // 隐藏边颜色
composer.addPass(outlinePass)
// 创建射线
const raycaster = new THREE.Raycaster()
// 创建鼠标向量，用于保存鼠标点在了画布的哪个地方
const mouse = new THREE.Vector2()
// 公共函数：更新鼠标位置并进行射线检测
function updateMouseAndRay(event) {
    // 阻止事件冒泡和默认行为
    event.preventDefault()
    event.stopPropagation()
    mouse.x = (event.clientX / window.innerWidth) * 2 - 1
    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
    raycaster.setFromCamera(mouse, camera)
    return raycaster.intersectObjects(Array.isArray(carRoot) ? carRoot : [carRoot])
}
// 处理左键单击变色逻辑
let selectedObject = null
function handleDoubleClick(event) {
    const intersects = updateMouseAndRay(event)
    if (intersects.length > 0 && event.button === 0 && dataSets.carContour) {
        selectedObject = intersects[0].object
        outlinePass.selectedObjects = [selectedObject] // 设置选中的物体
    } else {
        outlinePass.selectedObjects = [] // 清空选中物体
    }
}
// 处理右键点击变色逻辑
function handleRightClick(event) {
    const intersects = updateMouseAndRay(event)
    if (intersects.length > 0 && event.button === 2 && dataSets.changeColor) {
        intersects[0].object.material.color.set(dataSets.carbodyColor)
        intersects[0].object._isSelect = true
    }
}
// 绑定事件监听器
window.addEventListener('click', handleDoubleClick)
window.addEventListener('mousedown', handleRightClick)
// 恢复颜色
const restoreOriginalColors = () => {
    carRoot.traverse((child) => {
        if (child.isMesh && dataSets.originalColors[child.uuid]) {
            child.material.color.copy(dataSets.originalColors[child.uuid])
        }
    })
}


// 车衣 
const handleChange = (file,fileary) => {
    console.log(file)
    console.log(fileary)
    
    const reader = new FileReader()
    reader.onload = (e) => {
        // 获取文件的Data URL
        const textureURL = e.target.result
        const texture = new THREE.TextureLoader().load(textureURL) // 创建纹理对象
        // 需要展示车衣的子网格
        dataSets.coatPictures.forEach(child => {
            if (fileary.length>0) {
                child.material.map = texture  // 更新材质的纹理
                child.material.needsUpdate = true // 通知Three.js更新材质
                child.material.roughness = 0.5  // 较低的粗糙度
                child.material.metalness = 0.0  // 非金属表面
            }
            child.material = child.userData.originalMaterial

        })
    }
    // 读取文件
    reader.readAsDataURL(file.raw)
}

// 使用补间动画移动相机位置
const timeLine1 = gsap.timeline()
const timeLine2 = gsap.timeline()
// 定义相机移动函数
const translateCanera = (position, target) => {
    controls.enabled = false // 禁用控制器
    controls.enableDamping = false // 关闭阻尼效果
    // 要跳转到的位置
    timeLine1.to(camera.position, {
        x: position.x,
        y: position.y,
        z: position.z,
        duration: 1,
        ease: 'power2.inOut'
    }).eventCallback('onComplete', () => {
        controls.enabled = true // 动画完成后再启用控制器
        controls.enableDamping = true // 关闭阻尼效果
    })
    // 跳转时围绕哪个目标旋转
    timeLine2.to(controls.target, {
        x: target.x,
        y: target.y,
        z: target.z,
        duration: 1,
        ease: 'power2.inOut'
    })
}


let explosion = true
// 子网格发散动画
const createExplosion = () => {
    if (explosion) {
        explosion = false
        dataSets.carContour = true
        // 拉远相机视角
        translateCanera(new THREE.Vector3(-7.039684387919276, 2.9508311531241485, 6.516731871775105), new THREE.Vector3(0, 0, 0))
        // 使用GSAP控制动画，将小块沿不同方向发射
        carbody.forEach(chunk => {
            gsap.to(chunk.position, {
                x: chunk.position.x + Math.random() * 5 - 2.5,
                y: chunk.position.y + Math.random() * 5 - 2.5,
                z: chunk.position.z + Math.random() * 5 - 2.5,
                duration: 1.5,
                ease: 'power2.out'
            })
        })
    }
}
// 爆炸还原
const explosiveReduction = () => {
    explosion = true
    dataSets.carContour = false
    // 恢复相机视角
    translateCanera(new THREE.Vector3(-2.79626343630831, 1.1721123853734845, 2.5885392090220933), new THREE.Vector3(0, 0, 0))
    // 还原每个碎片的位置
    carbody.forEach((chunk) => {
        gsap.to(chunk.position, {
            x: 0,
            y: 0,
            z: 0,
            duration: 1.5,
            ease: 'power2.out'
        })
    })
}


// 车内视角跳转
const carNei = () => {
    translateCanera(new THREE.Vector3(0.2567136282590096, 0.9540817700797001, 0.08132888407229535), new THREE.Vector3(0.24886975258476365, 0.8774732822887555, 0.2932634858798176))
}
// 车外视角跳转
const carWai = () => {
    translateCanera(new THREE.Vector3(-2.79626343630831, 1.1721123853734845, 2.5885392090220933), new THREE.Vector3(0, 0, 0))
}


// 用于虚化的透明材质
const transparentMaterial = new THREE.MeshStandardMaterial({
    color: 0xADD8E6,// 设置颜色为蓝色
    transparent: true,// 启用透明
    opacity: 0.02,// 设置透明度
    depthWrite: false,// 禁用深度写入
    side: THREE.FrontSide,// 渲染物体的正面
    depthTest: true// 启用深度测试
})
// 模型虚化
function grammaticalization() {
    dataSets.gallardos.forEach(child => {
        child.material = transparentMaterial
        child.material.needsUpdate = true // 更新材质
    })
}
// 虚化还原
function restoremodel() {
    carRoot.traverse((child) => {
        if (child.isMesh) {
            child.material = child.userData.originalMaterial
        }
    })
}


// 封装开关门的通用函数
const openCloseDoor = (doorSet, isOpen, position, rotation) => {
    doorSet.forEach(door => {
        gsap.to(door.position, {
            x: isOpen ? position.x : 0,
            y: isOpen ? position.y : 0,
            z: isOpen ? position.z : 0,
            duration: 1,
            ease: 'power2.out'
        })
        gsap.to(door.rotation, {
            x: isOpen ? rotation.x : 0,
            y: isOpen ? rotation.y : 0,
            z: isOpen ? rotation.z : 0,
            duration: 1,
            ease: 'power2.out'
        })
    })
}
// 左侧开/关门动画
let lfetdoor = true  // 状态变量
const leftopenDoor = () => {
    openCloseDoor(dataSets.leftdoors, lfetdoor, { x: 0.35, y: 0, z: 0.55 }, { x: Math.PI / 6, y: -Math.PI / 12, z: 0 })
    lfetdoor = !lfetdoor  // 切换状态
}
// 右侧开/关门动画
let rightdoor = true  // 状态变量
const rightopenDoor = () => {
    openCloseDoor(dataSets.rightdoors, rightdoor, { x: -0.35, y: 0, z: 0.55 }, { x: Math.PI / 6, y: Math.PI / 12, z: 0 })
    rightdoor = !rightdoor  // 切换状态
}


// 车灯部分
const headlightMaterial = new THREE.MeshStandardMaterial({
    color: 0xFFFFFF,// 物体的基本颜色
    emissive: 0xFFFFFF,// 发光颜色（黄色）
    emissiveIntensity: 1.5,// 发光强度
    metalness: 0.5,// 是否金属
    roughness: 0.5// 粗糙度
})
const headlightMaterial2 = new THREE.MeshStandardMaterial({
    color: 'red', // 灯光颜色
    emissive: 'red', // 发光效果
    emissiveIntensity: 1.5, // 发光强度
    transparent: true,// 开启透明
    opacity: 0.1,// 设置透明值
    metalness: 0.5,// 是否金属
    roughness: 0.5// 粗糙度
})
let dengguang = true // 状态变量
const automativeLighting = () => {
    dataSets.headlights.forEach(child => {
        child.material = dengguang ? headlightMaterial : child.userData.originalMaterial
    })
    dataSets.taillights.forEach(child => {
        child.material = dengguang ? headlightMaterial2 : child.userData.originalMaterial
    })
    dengguang = !dengguang // 切换状态
}

// 设置画布自适应屏幕大小
window.addEventListener('resize', () => {
    render.setSize(window.innerWidth, window.innerHeight)
    camera.aspect = window.innerWidth / window.innerHeight
    camera.updateProjectionMatrix()
})

// 创建渲染函数
const animate = () => {
    // 请求动画帧
    requestAnimationFrame(animate)

    // 根据 rotateSpeed 调整纹理偏移
    roadTexture.offset.y = (roadTexture.offset.y - (dataSets.rotateSpeed / 1000)) % 1

    // 模拟轮毂移动
    dataSets.carhub.forEach((wheel) => {
        wheel.rotation.x += dataSets.rotateSpeed / 10
    })

    // 模拟轮胎向前运动
    dataSets.cartyre.forEach((wheel) => {
        wheel.rotation.y -= dataSets.rotateSpeed / 10
    })

    // 渲染
    render.render(scene, camera)
    controls.update()
    composer.render() // 使用composer渲染
}
// 调用渲染函数
animate()


</script>

<template>
    <dataPopup></dataPopup>
    <!-- 加载页面 -->
    <div id="loadingShade" v-show="loadingShade">
        <img src="../public/textures/img/loading.gif">
    </div>
    <!-- 控制面板 -->
    <div>
        <ul id="faceplate">
            <li>
                前进速度: <el-input-number v-model="dataSets.rotateSpeed" :min="0" :max="100" />
            </li>
            <li>
                车漆颜色: <el-color-picker v-model="dataSets.carbodyColor" />
                <el-button type="primary" @click="restoreOriginalColors">还原车身颜色</el-button>
            </li>
            <li>
                开启改色: <el-switch v-model="dataSets.changeColor" />
            </li>
            <li>
                <el-upload ref="upload" class="upload-demo"
                    action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15" :limit="5"
                    :auto-upload="false" :on-change="handleChange">
                    <template #trigger>
                        <el-button type="primary">上传图片</el-button>
                    </template>
                </el-upload>
            </li>
            <li>
                <el-button type="primary" @click="leftopenDoor()">左车门</el-button>
                <el-button type="primary" @click="rightopenDoor()">右车门</el-button>
            </li>
            <li>
                <el-button type="primary" @click="automativeLighting()">开启车灯</el-button>
                <!-- 开启车灯: <el-switch @click="automativeLighting()" v-model="dengguang" /> -->
            </li>
            <li>
                <el-button type="primary" @click="grammaticalization()">车身虚化</el-button>
                <el-button type="primary" @click="restoremodel()">车身还原</el-button>
            </li>
            <li>
                <el-button type="primary" @click="carNei()">车内视角</el-button>
                <el-button type="primary" @click="carWai()">车外视角</el-button>
            </li>
            <li>
                <el-button type="primary" @click="createExplosion()">车辆拆分</el-button>
                <el-button type="primary" @click="explosiveReduction()">车辆重组</el-button>
            </li>
        </ul>
    </div>
</template>

<style lang="scss" scoped>
// 遮罩
#loadingShade {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 5;
    width: 100vw;
    height: 100vh;
    background-color: rgba(0, 0, 0, 1);

    img {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
    }
}

#faceplate {
    position: fixed;
    right: 1%;
    top: 5%;
    z-index: 1;
    color: #fff;

    li {
        margin-bottom: 10px;

        input {
            border: none;
        }
    }
}
</style>